Skip to content

docs(core): SPECIFY_FEATURE sets the feature label, not the feature directory - #3786

Open
jawwad-ali wants to merge 3 commits into
github:mainfrom
jawwad-ali:docs/specify-feature-is-a-label
Open

docs(core): SPECIFY_FEATURE sets the feature label, not the feature directory#3786
jawwad-ali wants to merge 3 commits into
github:mainfrom
jawwad-ali:docs/specify-feature-is-a-label

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Problem

docs/reference/core.md (Environment Variables table) tells users:

SPECIFY_FEATURE — Override feature detection for non-Git repositories. Set to the feature directory name (e.g., 001-photo-albums) to work on a specific feature when not using Git branches.

That does not work. SPECIFY_FEATURE only feeds get_current_branch / Get-CurrentBranch — the feature label. The feature directory comes from SPECIFY_FEATURE_DIRECTORY or .specify/feature.json.

Verified on main with the real helper:

$ SPECIFY_FEATURE=001-photo-albums   ... get_feature_paths
ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or run the
       specify command to create .specify/feature.json.
exit=1

$ SPECIFY_FEATURE_DIRECTORY=specs/001-photo-albums ... get_feature_paths
FEATURE_DIR    -> <resolved>
CURRENT_BRANCH -> 001-photo-albums

So a user following the doc gets a hard failure — and the code's own error message points at the other variable.

The doc also contradicts itself: the "Two resolution axes" note immediately below the table already states that SPECIFY_FEATURE_DIRECTORY / .specify/feature.json select the feature, with no mention of SPECIFY_FEATURE.

Fix

Describe what the variable actually does: it sets the active feature label reported by the core helpers when there is no Git branch context, it is normally set for you by /speckit.specify and the Git extension, and it does not locate the directory — with the real error message quoted, and a pointer to the directory axis.

Docs only; no code change.

Verification

  • Behaviour confirmed against scripts/bash/common.sh (get_current_branch, get_feature_paths) and the PowerShell twin — I checked that Get-CurrentBranch exists and reads $env:SPECIFY_FEATURE before naming it.
  • markdownlint-cli2 error count on this file is identical before and after (1 pre-existing, untouched by this diff).

AI-assisted: authored with Claude Code. I ran both variables through the real get_feature_paths on main to capture the exact failure before rewriting the row, rather than reasoning from the source alone.

@jawwad-ali
jawwad-ali requested a review from mnriem as a code owner July 28, 2026 11:32
…irectory

docs/reference/core.md told users to set SPECIFY_FEATURE "to the feature
directory name ... to work on a specific feature when not using Git branches".
That does not work: SPECIFY_FEATURE only feeds get_current_branch /
Get-CurrentBranch (the feature *label*). The directory comes from
SPECIFY_FEATURE_DIRECTORY or .specify/feature.json.

Verified on main with the real helper -- with ONLY SPECIFY_FEATURE set:

    $ SPECIFY_FEATURE=001-photo-albums ... get_feature_paths
    ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or run
           the specify command to create .specify/feature.json.
    exit=1

    $ SPECIFY_FEATURE_DIRECTORY=specs/001-photo-albums ... get_feature_paths
    FEATURE_DIR    -> <resolved>
    CURRENT_BRANCH -> 001-photo-albums

The code's own error message points at the other variable, and the doc's own
"Two resolution axes" note directly below already says the feature is selected
by SPECIFY_FEATURE_DIRECTORY / .specify/feature.json -- so the table row
contradicted both the code and the paragraph under it.

Describe what the variable actually does, note that /speckit.specify and the
Git extension normally set it, and point at the directory axis. Docs only.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jawwad-ali
jawwad-ali force-pushed the docs/specify-feature-is-a-label branch from 4f0dd70 to 60720ea Compare July 28, 2026 14:56
@mnriem
mnriem requested a review from Copilot July 28, 2026 21:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Corrects documentation for feature-label and feature-directory resolution.

Changes:

  • Clarifies SPECIFY_FEATURE does not locate feature directories.
  • Documents the corresponding resolution error and alternatives.
Show a summary per file
File Description
docs/reference/core.md Updates environment-variable documentation.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread docs/reference/core.md Outdated

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback

The row still misstated when and how the label is applied:

* "when there is no Git branch context" — get_current_branch and
  Get-CurrentBranch never inspect Git at all. They return the variable
  verbatim when set, and otherwise fall back to the basename of the
  resolved feature directory.
* "Normally set for you by /speckit.specify" — specify.md persists
  feature_directory to .specify/feature.json and never sets this
  variable.
* The Bash and Python feature scripts can only *print* a commented
  export hint, because a child process cannot change its parent's
  environment. The PowerShell scripts do assign $env:SPECIFY_FEATURE,
  but only reach the caller when run inside the current session.

Rewrite it as an explicit user-set label override, and distinguish the
printed persistence hint from actually setting the caller's environment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (1)

docs/reference/core.md:55

  • The fallback is attributed to get_current_branch / Get-CurrentBranch, but both functions return an empty value when SPECIFY_FEATURE is unset (scripts/bash/common.sh:79-87, scripts/powershell/common.ps1:90-99). The basename fallback occurs later in get_feature_paths / Get-FeaturePaths after directory resolution. Please distinguish those behaviors so users calling the named helpers directly are not told they return the directory basename.
| `SPECIFY_FEATURE` | Explicitly override the active feature **label** (e.g. `001-photo-albums`) — the identifier the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell). Those helpers never inspect Git: when the variable is set they return it verbatim, and when it is unset the label falls back to the basename of the resolved feature directory. You set it yourself: the Bash and Python feature scripts only **print** a commented `export SPECIFY_FEATURE=…` / `$env:SPECIFY_FEATURE = …` hint for you to run, because a child process cannot change its parent's environment, and `/speckit.specify` persists `feature_directory` to `.specify/feature.json` instead of setting this variable. (The PowerShell feature scripts do assign `$env:SPECIFY_FEATURE`, but that only reaches you when the script runs inside your current PowerShell session.) It does **not** locate the feature directory: with only `SPECIFY_FEATURE` set, `get_feature_paths` fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. |
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

…t_current_branch

The row said the label "falls back to the basename of the resolved feature
directory" when SPECIFY_FEATURE is unset, and attributed that to
get_current_branch / Get-CurrentBranch. Those helpers return an EMPTY
string when the variable is unset — scripts/bash/common.sh:87 says so
outright ("Return empty to signal 'unknown'") and scripts/python/common.py
is `return os.environ.get("SPECIFY_FEATURE", "")`.

The basename substitution happens later, in get_feature_paths /
Get-FeaturePaths, after the feature directory has been resolved
(scripts/python/common.py:168-169). Measured:

  get_current_branch (unset)       -> []
  get_current_branch (set)         -> [my-label]
  get_feature_paths CURRENT_BRANCH -> [001-photo-albums]

So a caller invoking the named helpers directly does not get the fallback.
Distinguish the two behaviours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jawwad-ali

Copy link
Copy Markdown
Contributor Author

Addressed the suppressed low-confidence note from the 2026-07-30 review in b8a446d. It was correct, and it was my error.

My rewrite said the label "falls back to the basename of the resolved feature directory" when SPECIFY_FEATURE is unset, and attributed that to get_current_branch / Get-CurrentBranch. Those helpers return an empty string when the variable is unset — scripts/bash/common.sh:87 says so outright:

# No explicit feature set — caller must handle this via get_feature_paths().
# Return empty to signal "unknown".
echo ""

…and the Python helper is simply return os.environ.get("SPECIFY_FEATURE", ""). The basename substitution happens later, in get_feature_paths / Get-FeaturePaths, after the feature directory has been resolved (scripts/python/common.py:168-169).

Verified by running the helpers rather than reading them, in a project with .specify/feature.jsonspecs/001-photo-albums:

get_current_branch (unset)       -> []
get_current_branch (set)         -> [my-label]
get_feature_paths CURRENT_BRANCH -> [001-photo-albums]   <- fallback lives here

So your point stands exactly: someone calling the named helpers directly does not get the basename. The row now says they return an empty string when unset, and names get_feature_paths / Get-FeaturePaths as where the substitution happens.

Docs-only, still +1/-1 on one row. Table integrity re-checked programmatically (3 pipes / 2 cells, matching both neighbouring rows); markdownlint-cli2 reports the same single pre-existing MD028 as clean main (a blank line in an unrelated blockquote further down the file).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants